#pragma once
#include "fstream.h"
#include "http_data.h"
#include <chrono>
#include <cstddef>
#include <cstdio>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <string>
#include <unordered_map>

namespace co_async {
class HttpServer;
class WebSocketEndpoint;
class WebSocket;
using WsHandler =
    std::function<Task<bool>(WebSocketEndpoint &, const std::string &)>;
class WebSocketEndpoint {
public:
  friend class HttpServer;
  enum Opcode {
    CONTINUATION = 0x0,
    TEXT = 0x1,
    BINARY = 0x2,
    CLOSE = 0x8,
    PING = 0x9,
    PONG = 0xa
  };
  using Ptr = std::shared_ptr<WebSocketEndpoint>;
  WebSocketEndpoint(WebSocket *manager, size_t id, FileStream &&conn);
  ~WebSocketEndpoint();

  Task<bool> send(const std::string &payload);
  Task<Opcode> recv(std::string &msg);
  Task<> handle_ws();

private:
  FileStream m_conn;
  WebSocket *m_manager;
  size_t m_id;
  std::chrono::duration<double> m_timeout = std::chrono::seconds(10);
  bool m_ping = false;
  bool m_pong = false;
  Task<> ping();
};

class WebSocket {
public:
  friend class HttpServer;
  friend class WebSocketEndpoint;
  using Ptr = std::shared_ptr<WebSocket>;
  WebSocket(const std::string &path);

  void on_message(WsHandler task);
  static HttpResponse Handshake(const HttpRequest &req);
  static void Update(HttpServer &server, const std::string &path);
  WebSocketEndpoint::Ptr add_endpoint(FileStream &&conn);
  void remove_endpoint(size_t id);

protected:
  std::string m_path;
  size_t m_index;
  std::unordered_map<size_t, WebSocketEndpoint::Ptr> m_endpoints;
  WsHandler m_msg_cb;
};

} // namespace co_async